cargo-git-checkout \
cargo-test \
cargo-run \
- cargo-version
+ cargo-version \
+ cargo-new
SRC = $(shell find src -name '*.rs' -not -path 'src/bin*')
--- /dev/null
+#![feature(phase)]
+
+extern crate cargo;
+
+#[phase(plugin, link)]
+extern crate hammer;
+
+#[phase(plugin, link)]
+extern crate log;
+
+extern crate serialize;
+
+use std::os;
+use cargo::ops;
+use cargo::core::MultiShell;
+use cargo::util::{CliResult, CliError};
+
+#[deriving(PartialEq,Clone,Decodable,Encodable)]
+pub struct Options {
+ git: bool,
+ bin: bool,
+ rest: Vec<String>,
+}
+
+hammer_config!(Options "Create a new cargo project")
+
+fn main() {
+ cargo::execute_main_without_stdin(execute);
+}
+
+fn execute(options: Options, shell: &mut MultiShell) -> CliResult<Option<()>> {
+ debug!("executing; cmd=cargo-new; args={}", os::args());
+
+ let Options { git, mut rest, bin } = options;
+
+ let path = match rest.remove(0) {
+ Some(path) => path,
+ None => return Err(CliError::new("must have a path as an argument", 1))
+ };
+
+ let opts = ops::NewOptions {
+ git: git,
+ path: path.as_slice(),
+ bin: bin,
+ };
+
+ ops::new(opts, shell).map(|_| None).map_err(|err| {
+ CliError::from_boxed(err, 101)
+ })
+}
+
+
--- /dev/null
+use std::os;
+use std::io;
+use std::io::{fs, File, Command};
+
+use ops;
+use util::{CargoResult, human, ProcessError, Config, ChainError, process};
+use core::shell::MultiShell;
+use core::source::Source;
+use sources::PathSource;
+
+macro_rules! git( ($($a:expr),*) => ({
+ process("git") $(.arg($a))* .exec_with_output()
+}) )
+
+pub struct NewOptions<'a> {
+ pub git: bool,
+ pub bin: bool,
+ pub path: &'a str,
+}
+
+pub fn new(opts: NewOptions, shell: &mut MultiShell) -> CargoResult<()> {
+ let config = try!(Config::new(shell, false, None, None));
+ let path = os::getcwd().join(opts.path);
+ if path.exists() {
+ return Err(human(format!("Destination `{}` already exists",
+ path.display())))
+ }
+ let name = path.filename_str().unwrap();
+ mk(&path, name, &opts).chain_error(|| {
+ human(format!("Failed to create project `{}` at `{}`",
+ name, path.display()))
+ })
+}
+
+fn mk(path: &Path, name: &str, opts: &NewOptions) -> CargoResult<()> {
+
+ if opts.git {
+ try!(git!("init", path));
+ try!(File::create(&path.join(".gitignore")).write(b"/target\n"));
+ } else {
+ try!(fs::mkdir(path, io::UserRWX));
+ }
+
+ let author = try!(discover_author());
+ try!(File::create(&path.join("Cargo.toml")).write_str(format!(
+r#"[package]
+
+name = "{}"
+version = "0.0.1"
+authors = ["{}"]
+"#, name, author).as_slice()));
+
+ try!(fs::mkdir(&path.join("src"), io::UserRWX));
+
+ if opts.bin {
+ try!(File::create(&path.join("src/main.rs")).write_str("\
+fn main() {
+ println!(\"Hello, world!\")
+}
+"));
+ } else {
+ try!(File::create(&path.join("src/lib.rs")).write_str("\
+#[test]
+fn it_works() {
+}
+"));
+ }
+
+ Ok(())
+}
+
+fn discover_author() -> CargoResult<String> {
+ let name = match git!("config", "user.name") {
+ Ok(out) => String::from_utf8_lossy(out.output.as_slice()).into_string(),
+ Err(..) => match os::getenv("USER") {
+ Some(user) => user,
+ None => return Err(human("could not determine the current user, \
+ please set $USER"))
+ }
+ };
+
+ let email = match git!("config", "user.email") {
+ Ok(out) => Some(String::from_utf8_lossy(out.output.as_slice()).into_string()),
+ Err(..) => None,
+ };
+
+ let name = name.as_slice().trim().to_string();
+ let email = email.map(|s| s.as_slice().trim().to_string());
+
+ Ok(match (name, email) {
+ (name, Some(email)) => format!("{} <{}>", name, email),
+ (name, None) => name,
+ })
+}
pub use self::cargo_read_manifest::{read_manifest,read_package,read_packages};
pub use self::cargo_rustc::compile_targets;
pub use self::cargo_run::run;
+pub use self::cargo_new::{new, NewOptions};
mod cargo_clean;
mod cargo_compile;
mod cargo_read_manifest;
mod cargo_rustc;
mod cargo_run;
+mod cargo_new;
})
}
+ pub fn home(&self) -> &Path { &self.home_path }
+
pub fn git_db_path(&self) -> Path {
self.home_path.join(".cargo").join("git").join("db")
}
-use std::io::{File, TempDir};
+use std::io::File;
use support::{ProjectBuilder, ResultTest, project, execs, main_file, paths};
use support::{cargo_dir};
--- /dev/null
+use std::io::{fs, UserRWX, File};
+use std::os;
+
+use support::{execs, paths, cargo_dir, ResultTest};
+use hamcrest::{assert_that, existing_file, existing_dir};
+
+use cargo::util::{process, ProcessBuilder};
+
+fn setup() {
+}
+
+fn my_process(s: &str) -> ProcessBuilder {
+ process(s)
+ .cwd(paths::root())
+ .env("HOME", Some(paths::home()))
+}
+
+fn cargo_process(s: &str) -> ProcessBuilder {
+ process(cargo_dir().join(s))
+ .cwd(paths::root())
+ .env("HOME", Some(paths::home()))
+}
+
+test!(simple_lib {
+ os::setenv("USER", "foo");
+ assert_that(cargo_process("cargo-new").arg("foo"),
+ execs().with_status(0));
+
+ assert_that(&paths::root().join("foo"), existing_dir());
+ assert_that(&paths::root().join("foo/Cargo.toml"), existing_file());
+ assert_that(&paths::root().join("foo/src/lib.rs"), existing_file());
+
+ assert_that(cargo_process("cargo-build").cwd(paths::root().join("foo")),
+ execs().with_status(0));
+})
+
+test!(simple_bin {
+ os::setenv("USER", "foo");
+ assert_that(cargo_process("cargo-new").arg("foo").arg("--bin"),
+ execs().with_status(0));
+
+ assert_that(&paths::root().join("foo"), existing_dir());
+ assert_that(&paths::root().join("foo/Cargo.toml"), existing_file());
+ assert_that(&paths::root().join("foo/src/main.rs"), existing_file());
+
+ assert_that(cargo_process("cargo-build").cwd(paths::root().join("foo")),
+ execs().with_status(0));
+ assert_that(&paths::root().join(format!("foo/target/foo{}",
+ os::consts::EXE_SUFFIX)),
+ existing_file());
+})
+
+test!(simple_git {
+ os::setenv("USER", "foo");
+ assert_that(cargo_process("cargo-new").arg("foo").arg("--git"),
+ execs().with_status(0));
+
+ assert_that(&paths::root().join("foo"), existing_dir());
+ assert_that(&paths::root().join("foo/Cargo.toml"), existing_file());
+ assert_that(&paths::root().join("foo/src/lib.rs"), existing_file());
+ assert_that(&paths::root().join("foo/.git"), existing_dir());
+ assert_that(&paths::root().join("foo/.gitignore"), existing_file());
+
+ assert_that(cargo_process("cargo-build").cwd(paths::root().join("foo")),
+ execs().with_status(0));
+})
+
+test!(no_argument {
+ assert_that(cargo_process("cargo-new"),
+ execs().with_status(1)
+ .with_stderr("must have a path as an argument\n"));
+})
+
+test!(existing {
+ let dst = paths::root().join("foo");
+ fs::mkdir(&dst, UserRWX).assert();
+ assert_that(cargo_process("cargo-new").arg("foo"),
+ execs().with_status(101)
+ .with_stderr(format!("Destination `{}` already exists\n",
+ dst.display())));
+})
+
+test!(finds_author_user {
+ assert_that(cargo_process("cargo-new").arg("foo").env("USER", Some("foo")),
+ execs().with_status(0));
+
+ let toml = paths::root().join("foo/Cargo.toml");
+ let toml = File::open(&toml).read_to_string().assert();
+ assert!(toml.as_slice().contains(r#"authors = ["foo"]"#));
+})
+
+test!(finds_author_git {
+ my_process("git").args(["config", "--global", "user.name", "bar"])
+ .exec().assert();
+ my_process("git").args(["config", "--global", "user.email", "baz"])
+ .exec().assert();
+ assert_that(cargo_process("cargo-new").arg("foo").env("USER", Some("foo")),
+ execs().with_status(0));
+
+ let toml = paths::root().join("foo/Cargo.toml");
+ let toml = File::open(&toml).read_to_string().assert();
+ assert!(toml.as_slice().contains(r#"authors = ["bar <baz>"]"#));
+})
mod test_cargo_cross_compile;
mod test_cargo_run;
mod test_cargo_version;
+mod test_cargo_new;